/*
 * ToggleButton.java  0.4.0 / Oct 27, 2010
 *
 * Copyright (c) 2010 - 2011, cute4j.com and contributors.
 *
 * This software is licensed under the LGPL version 2.1 license.
 * A copy of this license is included in the file LICENSE-LGPL.txt.
 */
package com.cute4j.swing;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import com.cute4j.swing.properties.ToggleButtonProperties;

/**
 * Toggle Button Component.
 *
 * @cute4j.native
 *    generate="header"
 *
 * @author Slobodan Vrkacevic
 * @version 0.4.0
 */
public class ToggleButton extends AbstractButton
                          implements ToggleButtonProperties, Selectable {

// Package-Private Fields. -----------------------------------------------------

  boolean selected;
  SelectableGroup selectableGroup;

// Public Constructors. --------------------------------------------------------

  /**
   * Constructs new ToggleButton object.
   */
  public ToggleButton() {
    super();
  }

  /**
   * Constructs new ToggleButton object.
   *
   * @param action the action
   */
  public ToggleButton(Action action) {
    super(action);
  }

  /**
   * Constructs new ToggleButton object.
   *
   * @param text the button text
   */
  public ToggleButton(String text) {
    super(text);
  }

  /**
   * Constructs new ToggleButton object.
   *
   * @param icon the icon
   */
  public ToggleButton(Icon icon) {
    super(icon);
  }

  /**
   * Constructs new ToggleButton object.
   *
   * @param text the button text
   * @param icon the icon
   */
  public ToggleButton(String text, Icon icon) {
    super(text, icon);
  }

// Public Methods. -------------------------------------------------------------

  /**
   * Returns selected icon.
   *
   * @return selected icon
   */
  public Icon getSelectedIcon() {
    if (iconSet != null) {
      return iconSet.getSelectedIcon();
    }
    return null;
  }

  /**
   * Sets selected icon.
   *
   * @param icon the selected icon
   */
  public void setSelectedIcon(Icon icon) {
    disposedCheck();
    if (icon == null) {
      if (iconSet != null) {
        iconSet.setSelectedIcon(null, PROPERTY_SELECTED_ICON);
        if (iconSet.isEmpty()) {
          _setIcon(_delegate, null);
          iconSet = null;
        }
      }
    } else {
      if (iconSet == null) {
        iconSet = new SwingIconSet(this);
        _setIcon(_delegate, iconSet);
      }
      iconSet.setSelectedIcon(icon, PROPERTY_SELECTED_ICON);
      _setIconSize(_delegate, icon.getIconWidth(), icon.getIconHeight());
    }
  }

  /**
   * Returns disabled selected icon.
   *
   * @return disabled selected icon
   */
  public Icon getDisabledSelectedIcon() {
    if (iconSet != null) {
      return iconSet.getDisabledSelectedIcon();
    }
    return null;
  }

  /**
   * Sets disabled selected icon.
   *
   * @param icon the disabled selected icon
   */
  public void setDisabledSelectedIcon(Icon icon) {
    disposedCheck();
    if (icon == null) {
      if (iconSet != null) {
        iconSet.setDisabledSelectedIcon(null, PROPERTY_DISABLED_SELECTED_ICON);
        if (iconSet.isEmpty()) {
          _setIcon(_delegate, null);
          iconSet = null;
        }
      }
    } else {
      if (iconSet == null) {
        iconSet = new SwingIconSet(this);
        _setIcon(_delegate, iconSet);
      }
      iconSet.setDisabledSelectedIcon(icon, PROPERTY_DISABLED_SELECTED_ICON);
      _setIconSize(_delegate, icon.getIconWidth(), icon.getIconHeight());
    }
  }

  /**
   * Returns rollover selected icon.
   *
   * @return rollover selected icon
   */
  public Icon getRolloverSelectedIcon() {
    if (iconSet != null) {
      return iconSet.getRolloverSelectedIcon();
    }
    return null;
  }

  /**
   * Sets rollover selected icon.
   *
   * @param icon the disabled selected icon
   */
  public void setRolloverSelectedIcon(Icon icon) {
    disposedCheck();
    if (icon == null) {
      if (iconSet != null) {
        iconSet.setRolloverSelectedIcon(null, PROPERTY_ROLLOVER_SELECTED_ICON);
        if (iconSet.isEmpty()) {
          _setIcon(_delegate, null);
          iconSet = null;
        }
      }
    } else {
      if (iconSet == null) {
        iconSet = new SwingIconSet(this);
        _setIcon(_delegate, iconSet);
      }
      iconSet.setRolloverSelectedIcon(icon, PROPERTY_ROLLOVER_SELECTED_ICON);
      _setIconSize(_delegate, icon.getIconWidth(), icon.getIconHeight());
    }
  }

// Selectable Interface Implementation. ----------------------------------------

  /**
   * Returns <tt>true</tt> if the button is selected.
   *
   * @return <tt>true</tt> if the button is selected
   */
  public boolean isSelected() {
    return selected;
  }

  /**
   * Selected/unselect the button.
   *
   * @param selected <tt>true</tt> if selected
   */
  public void setSelected(boolean selected) {
    model.setSelected(selected);
  }

  /**
   * {@inheritDoc}
   */
  public SelectableGroup getSelectableGroup() {
    return selectableGroup;
  }

  /**
   * {@inheritDoc}
   */
  public void setSelectableGroup(SelectableGroup selectableGroup) {
    this.selectableGroup = selectableGroup;
  }

// Overridden Methods. ---------------------------------------------------------

  /**
   * {@inheritDoc}
   */
  protected void configurePropertiesFromAction(Action action) {
    if (action != null) {
      super.configurePropertiesFromAction(action);
      setSelected(action.isSelected());
    }
  }

  /**
   * {@inheritDoc}
   */
  protected PropertyChangeListener createActionPropertyChangeListener() {
    return new ActionPropertyChangeListener(this);
  }

  void processChangeEvent() {
    if (!blockModelEvents) {
      final ButtonModel model = this.model;
      final boolean enabled = model.isEnabled();
      if (this.enabled != enabled) {
        _setEnabled(_delegate, enabled);
      }
      final boolean pressed = model.isPressed();
      if (this.pressed != pressed) {
        _setPressed(_delegate, pressed);
        this.pressed = pressed;
      }
      final boolean selected = model.isSelected();
      if (this.selected != selected) {
        if (selectableGroup == null) {
          _setSelected(_delegate, selected);
          this.selected = selected;
        } else {
          if (selected) {
            this.selected = true;
            if (selectableGroup.setSelected(this)) {
              _setSelected(_delegate, true);
            } else {
              this.selected = false;
            }
          } else {
            // TODO: bug? - fix
            //if (selectableGroup.getSelected() != this) { // NOPMD
              _setSelected(_delegate, false);
              this.selected = false;
            //}
          }
        }
      }
    }
    fireStateChanged();
  }

  /*!
   * @cute4j.native
   *    overridden="true"
   */
  boolean _buttonPressed() {
    blockModelEvents = true;
    model.setPressed(true);
    if (!model.isPressed()) {
      blockModelEvents = false;
      return false;
    }
    pressed = true;
    if (model.isSelected()) {
      if ((selectableGroup != null) &&
          selectableGroup.getSelected() == this) { // NOPMD
        blockModelEvents = false;
        return false;
      }
      model.setSelected(false);
      if (model.isSelected()) {
        blockModelEvents = false;
        return false;
      }
      selected = false;
    } else {
      if (selectableGroup == null) {
        model.setSelected(true);
        if (!model.isSelected()) {
          blockModelEvents = false;
          return false;
        }
        selected = true;
      } else {
        selected = true;
        if (!selectableGroup.setSelected(this)) {
          selected = false;
          blockModelEvents = false;
          return false;
        }
        model.setSelected(true);
        if (!model.isSelected()) {
          selected = false;
          blockModelEvents = false;
          return false;
        }
      }
    }
    blockModelEvents = false;
    return true;
  }

// Package-Private Native Methods. ---------------------------------------------

  native long _create();

  native void _setSelected(long delegate, boolean selected);

// Inner Classes. --------------------------------------------------------------

  /**
   * Action PropertyChangeListener.
   */
  protected static class ActionPropertyChangeListener implements
                                                      PropertyChangeListener {

    protected ToggleButton button;

    /**
     * Constructs new ActionPropertyChangeListener object.
     *
     * @param button the button object
     */
    public ActionPropertyChangeListener(ToggleButton button) {
      this.button = button;
    }

    /**
     * {@inheritDoc}
     */
    public void propertyChange(PropertyChangeEvent e) {
      Action action = (Action)e.getSource();
      String propertyName = e.getPropertyName();
      if (AbstractAction.PROPERTY_ENABLED.equals(propertyName)) {
        button.setEnabled(action.isEnabled());
      } else if (AbstractAction.PROPERTY_SELECTED.equals(propertyName)) {
        button.setSelected(action.isSelected());
      } else if (AbstractAction.NAME.equals(propertyName)) {
        button.setText((String)action.getValue(AbstractAction.NAME));
      } else if (AbstractAction.SMALL_ICON.equals(propertyName)) {
        button.setIcon((Icon)action.getValue(AbstractAction.SMALL_ICON));
      } else if (AbstractAction.SHORT_DESCRIPTION.equals(propertyName)) {
        button.setToolTipText((String)action.getValue(
                                            AbstractAction.SHORT_DESCRIPTION));
      } else if (AbstractAction.MNEMONIC_KEY.equals(propertyName)) {
        Integer mnemonic = (Integer)action.getValue(
                                                  AbstractAction.MNEMONIC_KEY);
        if (mnemonic != null) {
          button.setMnemonic(mnemonic.intValue());
        }
      }
    }

  }

}
